/**
在 JavaScript 的响应式系统中，自动收集依赖是实现响应式编程的核心机制之一。在 Vue 3 中，依赖的自动收集使得开发者可以轻松地管理状态变化与视图更新。以下是对 Vue 3 中自动收集依赖的说明总结：

  1. 自动依赖收集的概念
  - 自动依赖收集：当一个响应式对象的属性在函数执行过程中被访问时，系统会自动记录下这个访问者（通常是计算属性或副作用函数）。当该属性发生变化时，所有依赖于它的函数都会被重新执行。
  - 这种机制使得开发者无需手动管理依赖关系，从而简化了代码的复杂性。

  2. 使用 Proxy 实现
  Vue 3 使用  `Proxy`  对对象进行代理，拦截对对象属性的访问和修改。具体的依赖收集流程如下：

  - 访问属性：当访问一个响应式对象的属性时，Vue 会触发  `getter` ，在这个过程中，它会记录当前的依赖（即正在执行的副作用函数）。
  - 记录依赖：在  `getter`  中，当前的副作用函数（如  `ReactiveEffect`  实例）会被注册到该属性的依赖列表中。
  - 触发更新：当该属性的值发生变化时，Vue 会触发  `setter` ，并遍历该属性的依赖列表，调用所有依赖的副作用函数，从而更新视图。

  3. 示例
  以下是一个简单的示例，展示了如何在 Vue 3 中实现自动依赖收集：
  import { reactive, effect } from 'vue';

  const state = reactive({ count: 0 });

  // 定义一个响应式的副作用
  effect(() => {
    console.log(`Count is: ${state.count}`);
  });

  // 修改响应式数据
  state.count++; // 当 count 变化时，副作用函数会被重新执行
  4. 效果与优势
  - 简化开发：开发者无需手动管理依赖关系，Vue 会自动处理依赖的注册与更新。
  - 性能优化：只有在依赖的响应式属性发生变化时，相关的副作用函数才会被重新执行，从而避免不必要的计算和渲染。
  - 清晰的逻辑：通过自动收集依赖，代码逻辑变得更加清晰，易于理解和维护。

  5. 注意事项
  - 依赖收集只会在响应式对象的属性被访问时进行，因此在副作用函数中确保访问了需要依赖的属性。
  - 如果副作用函数中不访问任何响应式属性，则不会进行依赖收集，也不会在属性变化时触发更新。

  总结
  在 Vue 3 中，自动收集依赖通过  `Proxy`  和响应式系统实现，使得开发者能够轻松管理状态变化与视图更新。理解自动依赖收集的机制对于掌握 Vue 3 的响应式特性至关重要，它简化了代码的复杂性，提高了开发效率。
 */
/**
 * 1.dep对象数据结构的管理(最难理解)
 * 每一个对象的每一个属性都会对应一个dep对象
 * 同一个对象的多个属性的dep对象是存放一个map对象中
 * 多个对象的map对象, 会被存放到一个objMap的对象中
 * 2.依赖收集: 当执行get函数, 自动的添加fn函数
 */
class Depend {
  constructor() {
    this.reactiveFns = new Set();
  }

  // 添加依赖
  addDepend(fn) {
    if (fn) {
      this.reactiveFns.add(fn);
    }
  }

  // 搜集依赖
  depend() {
    if (activeReactiveFn) {
      // this.addDepend(activeReactiveFn)
      this.reactiveFns.add(activeReactiveFn);
    }
  }

  // 通知更新
  notify() {
    this.reactiveFns.forEach((fn) => {
      fn();
    });
  }
}

const obj = {
  name: "responsive",
  age: 18,
};

// 设置一个专门执行响应式函数的一个函数
let activeReactiveFn = null;
function watchFn(fn) {
  activeReactiveFn = fn;
  fn();
  activeReactiveFn = null;
}

// 封装一个函数: 负责通过obj的key获取对应的Depend对象
const objMap = new WeakMap();
function getDepend(target, key) {
  // 1.根据对象obj, 找到对应的map对象
  let map = objMap.get(target);
  if (!map) {
    map = new Map();
    objMap.set(target, map);
  }

  // 2.根据key, 找到对应的depend对象
  let dep = map.get(key);

  if (!dep) {
    dep = new Depend();
    // console.log(dep, key, map);
    map.set(key, dep);
  }

  return dep;
}

// 方案一: Object.defineProperty() -> Vue2
Object.keys(obj).forEach((key) => {
  let value = obj[key];

  Object.defineProperty(obj, key, {
    get() {
      // 拿到obj -> key
      // console.log("get函数中:", obj, key)
      // 找到对应的obj对象的key对应的dep对象
      const dep = getDepend(obj, key);
      // dep.addDepend(activeReactiveFn);
      dep.depend();

      return value;
    },
    set(newValue) {
      value = newValue;
      const dep = getDepend(obj, key);
      dep.notify();
    },
  });
});

// 方案二: Proxy -> Vue3

// ========================= 业务代码 ========================

function foo() {
  console.log("foo function");
  console.log("foo:", obj.name);
  console.log("foo", obj.age);
}

function bar() {
  console.log("bar function");
  console.log("bar:", obj.age + 10);
}

watchFn(foo);
watchFn(bar);

// 修改obj的属性
// console.log("name发生变化-----------------------")
// obj.name = "kobe"
console.log("age发生变化-----------------------");
obj.age = 20;
